
/**
 * @file:       point_container.cpp
 * @author:     WubinWang
 * @contact:    wubinstu@163.com
 * @date:       2025-01-01
 * @license:    MIT License
 *
 * Copyright (c) 2025 WubinWang
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * DISCLAIMER:
 * 1. User acknowledges all application risks and consequences arising from usage
 *    of this software are exclusively borne by the user.
 * 2. The original author shall not be held liable for any direct, indirect,
 *    incidental, special, exemplary or consequential damages.
 * 3. No warranty of fitness for particular purpose or non-infringement is provided. */


#include "point_container.hpp"

#include <utility>

Linear_Container_ct::Linear_Container_ct (const Linear_Container_ct & _other) noexcept :
    _m_line_id {_other._m_line_id} {
    this->_m_line_list.clear ();
    for (const auto & point_ptr : _other._m_line_list)
        if (point_ptr) this->_m_line_list.emplace_back (point_ptr->clone ());
}

Linear_Container_ct & Linear_Container_ct::operator= (const Linear_Container_ct & _other) noexcept {
    if (this != &_other) {
        _m_line_id = _other._m_line_id;
        this->_m_line_list.clear ();
        for (const auto & point_ptr : _other._m_line_list)
            if (point_ptr) this->_m_line_list.emplace_back (point_ptr->clone ());
    }
    return *this;
}

Linear_Container_ct::Linear_Container_ct (Linear_Container_ct && _other) noexcept :
    _m_line_id {_other._m_line_id}, _m_line_list {std::move (_other._m_line_list)} {
    _other._m_line_id = 0;
}

Linear_Container_ct & Linear_Container_ct::operator= (Linear_Container_ct && _other) noexcept {
    if (this != &_other) {
        _m_line_id         = _other._m_line_id;
        this->_m_line_list = std::move (_other._m_line_list);
        _other._m_line_id  = 0;
    }
    return *this;
}

Linear_Container_ct::Linear_Container_ct (std::uint16_t _line_id) noexcept :
    _m_line_id {_line_id} {
    this->init (_line_id);
};

auto Linear_Container_ct::init (std::uint16_t _id) -> void {
    this->_m_line_id = _id;
    this->_m_line_list.clear ();
}

auto Linear_Container_ct::counts () -> std::uint16_t {
    return static_cast<std::uint16_t> (this->_m_line_list.size ());
}

auto Linear_Container_ct::id () -> std::uint16_t & {
    return this->_m_line_id;
}

auto Linear_Container_ct::get (std::uint16_t _no, Point_Base_st ** _pb) -> Point_Base_st * {
    for (auto & ptr : this->_m_line_list) {
        if (ptr->_no == _no) {
            if (_pb) *_pb = ptr.get ();
            return ptr.get ();
        }
        if (ptr->_no > _no) break;
    }
    if (_pb) *_pb = nullptr;
    return nullptr;
}

auto Linear_Container_ct::append (const Point_Base_st & _pb) -> void {
    auto new_point = _pb.clone ();
    if (this->_m_line_list.empty ())
        new_point->_no = POINT_NO_MIN;
    else new_point->_no = this->_m_line_list.back ()->_no + 1;
    this->_m_line_list.push_back (std::move (new_point));
}

auto Linear_Container_ct::insert (std::uint16_t _no, const Point_Base_st & _pb) -> void {
    auto new_point = _pb.clone ();
    new_point->_no = _no;

    auto it        = this->_m_line_list.begin ();
    while (it != this->_m_line_list.end () && (*it)->_no < _no) ++it;

    it = this->_m_line_list.insert (it, std::move (new_point));
    ++it;

    for (; it != this->_m_line_list.end (); ++it)
        (*it)->_no += 1;
}

auto Linear_Container_ct::replace (std::uint16_t _no, const Point_Base_st & _pb) -> void {
    auto new_point = _pb.clone ();
    new_point->_no = _no;

    for (auto & ptr : this->_m_line_list) {
        if (ptr->_no == _no) {
            ptr.swap (new_point);
            return;
        }
        if (ptr->_no > _no) break;
    }
}

auto Linear_Container_ct::remove (std::uint16_t _no) -> void {
    for (auto it = this->_m_line_list.begin (); it != this->_m_line_list.end ();) {
        if ((*it)->_no == _no) {
            it = this->_m_line_list.erase (it);
            for (; it != this->_m_line_list.end (); ++it)
                (*it)->_no -= 1;
            return;
        } else if ((*it)->_no > _no) break;
        else ++it;
    }
}

//============================================================================================//
//============================================================================================//
//============================================================================================//


auto MultiLinear_Container_ct::init () -> void {
    this->_m_lines.clear ();
}

auto MultiLinear_Container_ct::counts () -> std::uint16_t {
    return static_cast<std::uint16_t> (this->_m_lines.size ());
}

auto MultiLinear_Container_ct::has (std::uint16_t _line_id) -> bool {
    return this->_m_lines.find (_line_id) != this->_m_lines.end ();
}

auto MultiLinear_Container_ct::touch (std::uint16_t _line_id) -> bool {
    if (this->has (_line_id)) return true;
    this->_m_lines.emplace (_line_id, Linear_Container_ct {_line_id});
    return false;
}

auto MultiLinear_Container_ct::get (std::uint16_t _line_id, std::uint16_t _no, Point_Base_st ** _pb) -> Point_Base_st * {
    auto it = this->_m_lines.find (_line_id);
    if (it == this->_m_lines.end ()) {
        if (_pb) *_pb = nullptr;
        return nullptr;
    }
    return it->second.get (_no, _pb);
}

auto MultiLinear_Container_ct::append (std::uint16_t _line_id, const Point_Base_st & _pb) -> bool {
    auto it = this->_m_lines.find (_line_id);
    if (it == this->_m_lines.end ()) return false;
    it->second.append (_pb);
    return true;
}

auto MultiLinear_Container_ct::insert (std::uint16_t _line_id, std::uint16_t _no, const Point_Base_st & _pb) -> bool {
    auto it = this->_m_lines.find (_line_id);
    if (it == this->_m_lines.end ()) return false;
    it->second.insert (_no, _pb);
    return true;
}

auto MultiLinear_Container_ct::replace (std::uint16_t _line_id, std::uint16_t _no, const Point_Base_st & _pb) -> bool {
    auto it = this->_m_lines.find (_line_id);
    if (it == this->_m_lines.end ()) return false;
    it->second.replace (_no, _pb);
    return true;
}

auto MultiLinear_Container_ct::remove (std::uint16_t _line_id, std::uint16_t _no) -> bool {
    auto it = this->_m_lines.find (_line_id);
    if (it == this->_m_lines.end ()) return false;
    it->second.remove (_no);
    return true;
}

auto MultiLinear_Container_ct::operator[] (std::uint16_t _line_id) -> Linear_Container_ct & {
    auto it = this->_m_lines.find (_line_id);
    if (it == this->_m_lines.end ()) {
        this->_m_lines.emplace (_line_id, Linear_Container_ct {_line_id});
        it = this->_m_lines.find (_line_id);
    }
    return it->second;
}

auto MultiLinear_Container_ct::push (Linear_Container_ct && _line) -> bool {
    auto line_id = _line.id ();
    return this->_m_lines.emplace (line_id, std::move (_line)).second;
}

auto MultiLinear_Container_ct::pop (std::uint16_t _line_id) -> void {
    this->_m_lines.erase (_line_id);
}

auto MultiLinear_Container_ct::updateLineID (std::uint16_t _old_line_id, std::uint16_t _new_line_id) -> bool {

    auto old_it = this->_m_lines.find (_old_line_id);
    if (old_it == this->_m_lines.end ()) return false;

    auto new_it = this->_m_lines.find (_new_line_id);
    if (new_it != this->_m_lines.end ()) return false;

    Linear_Container_ct line {std::move (old_it->second)};
    line.id () = _new_line_id;

    this->_m_lines.emplace (_new_line_id, std::move (line));
    this->_m_lines.erase (old_it);

    return true;
}
